home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Testing & Debugging / General tools / Report Error 2.0 / FailureHandler.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-17  |  6.1 KB  |  242 lines  |  [TEXT/KAHL]

  1. /*============================================================
  2.     FailureHander.c
  3.     
  4.     greggor@apple.com
  5.     
  6.     The API of this package is similar to the one used
  7.     internally by the System 7 Finder.  The implementation
  8.     is different, though.
  9.     
  10.     Usage:
  11.     
  12.     #include "FailureHandler.h"
  13.     
  14.         ptr1 = nil;
  15.         ptr2 = nil;
  16.  
  17.         TRY
  18.         {
  19.             ptr1 = NewPtr( 1024 * 1024 );
  20.             FailMemError();
  21.             ptr2 = NewPtr( 15 );
  22.             FailMemError();
  23.             
  24.             FailOSErr( SomeFnReturningOSErr() );
  25.         }
  26.         EXCEPT
  27.         {
  28.             //    Clean up here
  29.             
  30.             if( ptr1 != nil )
  31.                 DisposPtr( ptr1 );
  32.             if( ptr2 != nil )
  33.                 DisposPtr( ptr2 );
  34.         }
  35.         ENDTRY
  36.     
  37. ============================================================*/
  38. #include <Types.h>
  39. #include <Memory.h>
  40. #include <Resources.h>
  41. #include <setjmp.h>
  42.  
  43. #include "FailureHandler.h"
  44.  
  45. #define DEBUGMESSAGES
  46. /*
  47. #define FAILMESSAGES
  48. */
  49.  
  50. #define kResignalingFailure        0x10
  51.  
  52. FailStack*            gFailStack = nil;
  53. NotifyFailureProc    gNotifyProc = nil;
  54.  
  55. /*-------------------------------------------------------------------
  56.     This function is called by the TRY macro
  57. -------------------------------------------------------------------*/
  58. void SetupTry( FailStack* newFailHandler )
  59. {
  60.     newFailHandler->magicFailID = kMagicFailID;
  61.     /*
  62.     // Clear out the 'error' & 'flags' entries
  63.     */
  64.     newFailHandler->error = noErr;
  65.     newFailHandler->flags = 0;
  66.     /*
  67.     // Push the new entry onto the failure stack
  68.     */
  69.     newFailHandler->next = gFailStack;
  70.     gFailStack = newFailHandler;
  71. }
  72.  
  73. /*-------------------------------------------------------------------
  74.     'PopFailureStack' is called by the ENDTRY macro
  75. -------------------------------------------------------------------*/
  76. void PopFailureStack()
  77. {
  78.     if( (gFailStack != nil) && (gFailStack->magicFailID == kMagicFailID) )
  79.     {
  80.         /*
  81.         // Pop an entry off the top of the fail stack
  82.         */
  83.         gFailStack = gFailStack->next;
  84.     }
  85.     #ifdef DEBUGMESSAGES
  86.         else
  87.         {
  88.             if( gFailStack == nil )
  89.                 DebugStr( "\pError -- ENDTRY without TRY" );
  90.             else
  91.                 DebugStr( "\pFatal error in PopFailureStack--failure handler stack corrupted" );
  92.         }
  93.     #endif
  94. }
  95.  
  96. /*-------------------------------------------------------------------
  97.     Invoke exception handling if 'theErr' != noErr
  98. -------------------------------------------------------------------*/
  99. void FailOSErr( OSErr theErr )
  100. {
  101.     if( theErr != noErr )
  102.         Failure( theErr );
  103. }
  104.  
  105. /*-------------------------------------------------------------------
  106.     Invoke exception handling
  107. -------------------------------------------------------------------*/
  108. void Failure( OSErr theErr )
  109. {
  110.     #ifdef FAILMESSAGES
  111.         DebugPrintf( "Failure caused by an error %d", theErr );
  112.     #endif
  113.     /*
  114.     // First do a little bit of sanity checking
  115.     */
  116.     if( (gFailStack != nil) && (gFailStack->magicFailID == kMagicFailID) )
  117.     {
  118.         /*
  119.         // If we are already in a failure handler, call
  120.         // 'FailAgain'; don't go back to the same handler!
  121.         */
  122.         if( gFailStack->flags & kInExceptionHandler )
  123.             FailAgain();
  124.         /*
  125.         // Remember the error that caused the failure &
  126.         // set a bit that indicates that we have invoked
  127.         // exception handling
  128.         */
  129.         gFailStack->error = theErr;
  130.         gFailStack->flags |= kInExceptionHandler;
  131.         /*
  132.         // Record the stack chain in case someone is
  133.         // interested in it later.  Don't blast the
  134.         // old stack chain if we are resignalling, though!
  135.         */
  136.         if( (gNotifyProc != nil) && ((gFailStack->flags & kResignalingFailure) == 0) )
  137.             (*gNotifyProc)();
  138.         
  139.         /*
  140.         // TRY does a setjmp; now we longjmp back to
  141.         // that point with a longjmp.  The 'TRY' macro
  142.         // includes an 'if' statement that is true
  143.         // the first time through and false when we longjmp
  144.         // back to it; the EXCEPTion handler is just the
  145.         // 'else' branch of that 'if' block.
  146.         */
  147.         longjmp( gFailStack->env, 1 );
  148.     }
  149.     #ifdef DEBUGMESSAGES
  150.         else
  151.         {
  152.             if( gFailStack == nil )
  153.                 DebugStr( "\pFatal error in FailOSErr--gFailStack nil" );
  154.             else
  155.                 DebugStr( "\pFatal error in FailOSErr--failure handler stack corrupted" );
  156.         }
  157.     #endif
  158. }
  159.  
  160. /*-------------------------------------------------------------------
  161.     Return the error code that caused the failure.
  162.  
  163.     It is only legal to request TheFailError within an EXCEPT block
  164. -------------------------------------------------------------------*/
  165. OSErr TheFailError()
  166. {
  167.     if( gFailStack == nil )
  168.         return ePointerNil;
  169.  
  170.     #ifdef DEBUGMESSAGES
  171.         if( gFailStack->magicFailID != kMagicFailID )
  172.             DebugStr( "\pFatal error in TheFailError--failure handler stack corrupted" );
  173.     #endif
  174.         
  175.     return gFailStack->error;
  176. }
  177.  
  178. /*-------------------------------------------------------------------
  179.     It is only legal to call 'FailAgain' from within an EXCEPT
  180.     block.  If called, FailAgain will pass the error that
  181.     caused the exception failure on to the failure handler
  182.     above this one.
  183. -------------------------------------------------------------------*/
  184. void FailAgain()
  185. {
  186.     OSErr    theErr;
  187.     
  188.     /*
  189.     // Check for programming errors
  190.     */
  191.     if( (gFailStack != nil) && (gFailStack->next != nil) )
  192.     {
  193.         /*
  194.         // Get the error; make sure it is not 'noErr'
  195.         */
  196.         theErr = TheFailError();
  197.         if( theErr == noErr )
  198.             theErr = eGeneralErr;
  199.         /*
  200.         // Pop off this failure handler and fail again
  201.         */
  202.         PopFailureStack();
  203.         gFailStack->flags |= kResignalingFailure;
  204.         Failure( theErr );
  205.     }
  206.     #ifdef DEBUGMESSAGES
  207.         else
  208.         {
  209.             DebugStr( "\pBad call to FailAgain" );
  210.         }
  211.     #endif
  212. }
  213.  
  214. /*-------------------------------------------------------------------
  215.     Install a single routine called at failure time before
  216.     exception processing begins.  This function is used in
  217.     conjunction with ShowStackChain to record and later show
  218.     the stack at the time of failure.
  219. -------------------------------------------------------------------*/
  220. void SetExceptionNotifyProc(NotifyFailureProc notifyProc)
  221. {
  222.     gNotifyProc = notifyProc;
  223. }
  224.  
  225. /*-------------------------------------------------------------------
  226.     This routine is used by ReportError so that it knows whether or
  227.     not it should show the 'sc6' button
  228. -------------------------------------------------------------------*/
  229. Boolean ExceptionNotifyProcInstalled(void)
  230. {
  231.     return gNotifyProc != nil;
  232. }
  233.  
  234. /*-------------------------------------------------------------------
  235.     This routine doesn't actually do anything; it's just used
  236.     by the NOREGISTER macro
  237. -------------------------------------------------------------------*/
  238. void MakeVariableNoRegister( void* foo )
  239. {
  240.  
  241. }
  242.